unit FDirectory;

interface

uses
  SysUtils,FStrUtiles,EntSal,Classes;

type
  IDirectory = interface
  end;

  TFile = class(Tobject)
  private
    AName:String;
    AFullName:String;
    AExtension:String;
    ASize:Integer;
    AIsHidden:Boolean;
    AIsSystemFile:Boolean;
    AIsReadOnly:Boolean;
    AExists:Boolean;
    procedure setName(value:String);
    procedure setFullName(value:String);
    procedure setExtension(value:String);
    procedure setSize(value:Integer);
    procedure setIsHidden(value:Boolean);
    procedure setIsSystemFile(value:Boolean);
    procedure setIsReadOnly(value:Boolean);
    procedure setExists(value:Boolean);
    procedure Inicializar;
    procedure Clone(var archivoDestino:TFile);
  public
    property Name:String Read AName Write setName;
    property FullName:String Read AFullName Write setFullName;
    property Size:Integer Read Asize Write setSize;
    property IsHidden:Boolean Read AIsHidden Write setIsHidden;
    property IsSystemFile:Boolean Read AIsSystemFile write setIsSystemFile;
    property IsReadOnly:Boolean Read AIsReadOnly write setIsReadOnly;
    property Exists:Boolean Read AExists write setExists;
    Constructor Create;overload;
    Constructor Create(path:String);overload;
  published
    property Extension:String Read AExtension;
  end;

  TFiles = Array Of TFile;

  TDirectoryItem = class(TObject)
  private
    AName:String;                                   
    AFullName:String;
    AExists:Boolean;
    procedure setName(value:String);
    procedure setFullName(value:String);
    procedure setExists(value:Boolean);
    procedure Inicializar;
  public
    property Name:String Read AName Write setName;
    property FullName:String read AFullName Write setFullName;
    property Exists:Boolean read AExists Write setExists;
    Constructor Create; Overload;
    Constructor Create(path:String); Overload;
  end;

  TDirectories = Array of TDirectoryItem;

  TDirectory = class(TDirectoryItem)	//class(TInterfacedObject,IDirectory)
  private
    APath:String;
    ArDirectories:TDirectories;
    ArFiles:TFiles;
    procedure setPath(Value:String);
    function validarRuta:Boolean;
  public
    property Path:String Read APath Write setPath;
    function Exists():Boolean;
    function getDirectories():TDirectories;
    function getFiles(extensiones:String=''):TFiles;
    function getAllFiles(extensiones:String=''):TFiles;
    function extraerArchivos(path:String;extensiones:String=''):TFiles;
  end;

implementation

  {TFile}

  Constructor TFile.Create;
  begin
    inherited;
  end;

  Constructor TFile.Create(path:String);
  begin
    self.AFullName := path;
    self.Inicializar;    
  end;

  procedure TFile.setName(value:String);
  begin
    self.AName := value;
    self.setExtension(value);
  end;
  procedure TFile.setFullName(value:String);
  begin
    self.AFullName := value;
  end;
  procedure TFile.setExtension(value:String);
  var posDot:Integer;
    tmpExt:String;
  begin
    tmpExt := '';
    posDot:= getIndexPosFromSTR(value,'.',length(value),True);
    if posDot>-1 then tmpExt:=StrRight(value,Length(value)-posDot);
    self.AExtension := tmpExt;
  end;
  procedure TFile.setSize(value:Integer);
  begin
    self.ASize := value;
  end;
  procedure TFile.setIsHidden(value:Boolean);
  begin
    self.AIsHidden := value;
  end;
  procedure TFile.setIsSystemFile(value:Boolean);
  begin
    self.AIsSystemFile := value;
  end;
  procedure TFile.setIsReadOnly(value:Boolean);
  begin
    self.AIsReadOnly := value;
  end;
  procedure TFile.setExists(value:Boolean);
  begin
    self.Exists := value;
  end;

  procedure TFile.Inicializar;
  var fSlash:Integer;
      attr:Integer;
      archivo:File;
  begin
    //Inicializa un Archivo File.
    if self.AFullName<>'' then begin
      self.AExists := FileExists(self.AFullName);
      fslash := getIndexPosFromSTR(self.AFullName,'\',length(self.AFullName),True);
      self.Name := STRRight(self.AFullName,Length(self.FullName)-fslash);
      if self.AExists then begin        
        attr := FileGetAttr(self.AFullName);
        if (attr and faReadOnly) > 0 then self.AIsReadOnly := True
        else self.AIsReadOnly := False;
        if (attr and faSysFile) > 0 then self.AIsSystemFile := True
        else self.AIsSystemFile := False;
        if (attr and faHidden) > 0 then self.AIsHidden := True
        else self.AIsHidden := False;
        self.ASize := TamanoArchivo(self.FullName);
      end else begin
        self.AIsReadOnly  := false;
        self.AIsSystemFile:=false;
        self.AIsHidden    := false;
      end;
    end;
  end;

  procedure TFile.Clone(var archivoDestino:TFile);
  begin
    //Clona el archivo.
    archivoDestino := TFile.Create(self.FullName);
  end;

{TDirectoryItem}

  Constructor TDirectoryItem.Create;
  begin
    inherited;
    self.AFullName := '';
  end;

  Constructor TDirectoryItem.Create(path:String);
  begin
    self.AFullName := path;
  end;

  procedure TDirectoryItem.Inicializar;
  begin
    //Inicializa el Directorio.
    if self.AFullName <> '' then begin
    end;
  end;

  procedure TDirectoryItem.setName(value:String);
  begin
    self.AName:=value;
  end;
  procedure TDirectoryItem.setFullName(value:String);
  begin
    self.AFullName := value;
  end;
  procedure TDirectoryItem.setExists(value:Boolean);
  begin
    self.AExists := value;
  end;

  {TDirectory}

  procedure TDirectory.setPath(Value:String);
  begin
    APath := Value;
  end;

  function TDirectory.validarRuta:Boolean;
  begin
    If self.APath = '' then begin
      raise Exception.Create('Error. No se ha declarado la Ruta (Path)');
      Result := False;
      Exit;
    end;
    Result := True;
  end;

  Function TDirectory.Exists:Boolean;
  begin
    //Regresa verdadero si encuentra la carpeta
    if self.validarRuta then begin
      Result := DirectoryExists(self.APath);
      exit;
    end;
    Result := False;
  end;

  function TDirectory.getDirectories:TDirectories;
  var Rec:TSearchRec;
    Contador :Integer;
  begin
    if not self.validarRuta then Exit;
    Contador := 0;

    if FindFirst(self.APath + '\*', faAnyFile, Rec) = 0 then
      try
        repeat
          if (Rec.Name = '.') or (Rec.Name = '..') then
            continue;
          if (Rec.Attr and faVolumeID) = faVolumeID then
            continue; // nothing useful to do with volume IDs
          if (Rec.Attr and faHidden) = faHidden then
            continue; // honor the OS "hidden" setting
          if (Rec.Attr and faDirectory) = faDirectory then begin
            inc(Contador,1);
            setLength(self.ArDirectories,Contador);
            self.ArDirectories[Contador -1] := TDirectoryItem.Create;
            self.ArDirectories[Contador -1].setName(Rec.Name);
            self.ArDirectories[Contador - 1].setFullName(self.APath + '\' + Rec.Name);
          end;
        until FindNext(Rec) <> 0;
      finally
        SysUtils.FindClose(Rec);
      end;
    Result := self.ArDirectories;
  end;

  function TDirectory.getFiles(extensiones:String=''):TFiles;
  var listaEx:TStringList;
    Rec:TSearchRec;
    Contador:Integer;
    tmpFile : TFile;
  begin
    //Retorna los archivos
    if trim(extensiones) <> '' then listaEx := SplitStr(UC(extensiones),',');
    
    if not self.validarRuta then Exit;
    Contador := 0;
    if FindFirst(self.APath + '\*', faAnyFile, Rec)=0 then begin
      try
        repeat
          if ((Rec.Attr and faArchive) = faArchive) then begin
            tmpFile := TFile.Create(self.Path+'\'+Rec.Name);
            if trim(extensiones) <> '' then
              if not existsIn(UC(tmpFile.Extension),listaEx) then Continue;
            inc(Contador,1);
            setLength(self.ArFiles,Contador);
            self.ArFiles[Contador - 1] := TFile.Create(self.Path+'\'+Rec.Name);
          end else begin
            Continue;
          end;
        until Findnext(Rec)<>0;
      finally
        Sysutils.FindClose(Rec);
      end;
    end;
    Result := self.ArFiles;
  end;

  function TDirectory.getAllFiles(extensiones:String=''):TFiles;  
  begin
    //Retorna todos los archivos.
    Result := self.extraerArchivos(self.Path,extensiones);
  end;

  function TDirectory.extraerArchivos(path:String;extensiones:String=''):TFiles;
  var carpeta:TDirectory; archivos:TFiles; subcarpetas:TDirectories;
    i,j:Integer;
    subFiles, tmpArch:TFiles;
  begin
    //Exrae los archivos.
    carpeta := TDirectory.Create;
    carpeta.setPath(path);
    subcarpetas := carpeta.getDirectories;
    archivos := carpeta.getFiles(extensiones);
    for i:=0 to (Length(subcarpetas) - 1) do begin
      subFiles := self.extraerArchivos(subcarpetas[i].FullName);
      for j:=0 to (Length(subFiles)-1) do begin
        setLength(tmpArch,Length(tmpArch)+1);
        subFiles[j].Clone(tmpArch[Length(tmpArch)-1]);
      end;
    end;
    for i:=0 to (Length(archivos) - 1) do begin
      setLength(tmpArch, Length(tmpArch)+1);
      archivos[i].Clone(tmpArch[Length(tmpArch)-1]);
      archivos[i].Free;
    end;
    carpeta.Free;
    Result := tmpArch;
  end;
  
end.

{
faAnyFile 	: Any file
faReadOnly 	: Read-only files
faHidden 	: Hidden files
faSysFile 	: System files
faVolumeID 	: Volume ID files
faDirectory 	: Directory files
faArchive 	: Archive files
}
